package com.nfc.lock.service.impl;

import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.nfc.lock.common.constant.Constants;
import com.nfc.lock.common.exception.BusinessException;
import com.nfc.lock.common.result.ResultCode;
import com.nfc.lock.dto.request.LockOperateRequest;
import com.nfc.lock.dto.response.LockListVO;
import com.nfc.lock.entity.NfcLock;
import com.nfc.lock.entity.OperationLog;
import com.nfc.lock.entity.PowerRoom;
import com.nfc.lock.entity.User;
import com.nfc.lock.entity.WarningRecord;
import com.nfc.lock.mapper.NfcLockMapper;
import com.nfc.lock.mapper.OperationLogMapper;
import com.nfc.lock.mapper.PowerRoomMapper;
import com.nfc.lock.mapper.UserMapper;
import com.nfc.lock.mapper.WarningRecordMapper;
import com.nfc.lock.security.SecurityUtils;
import com.nfc.lock.service.INfcLockService;
import com.nfc.lock.service.IUserService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.time.Duration;
import java.time.LocalDateTime;

/**
 * 锁具业务实现.
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class NfcLockServiceImpl extends ServiceImpl<NfcLockMapper, NfcLock> implements INfcLockService {

    private final NfcLockMapper nfcLockMapper;
    private final PowerRoomMapper powerRoomMapper;
    private final UserMapper userMapper;
    private final OperationLogMapper operationLogMapper;
    private final WarningRecordMapper warningRecordMapper;
    private final IUserService userService;

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void addLock(String lockNfcId, Long roomId) {
        if (nfcLockMapper.selectByNfcId(lockNfcId) != null) {
            throw new BusinessException(ResultCode.LOCK_NFC_ID_EXIST);
        }

        PowerRoom room = powerRoomMapper.selectById(roomId);
        if (room == null) {
            throw new BusinessException(ResultCode.ROOM_NOT_EXIST);
        }

        Long currentUserId = SecurityUtils.getCurrentUserId();
        if (!userService.checkPermission(currentUserId, room.getRegionId())) {
            throw new BusinessException(ResultCode.FORBIDDEN);
        }

        NfcLock lock = new NfcLock();
        lock.setLockNfcId(lockNfcId);
        lock.setRoomId(roomId);
        lock.setLockStatus(Constants.LockStatus.CLOSED);
        lock.setIsDamaged(0);
        lock.setCreateBy(currentUserId);
        nfcLockMapper.insert(lock);

        saveOperationLog(lock, Constants.OperationType.ADD, null, null, "新增锁具");
        log.info("新增锁具成功: {}", lockNfcId);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void openLock(LockOperateRequest request) {
        NfcLock lock = nfcLockMapper.selectByNfcId(request.getLockNfcId());
        if (lock == null) {
            throw new BusinessException(ResultCode.LOCK_NOT_EXIST);
        }

        if (lock.getIsDamaged() != null && lock.getIsDamaged() == 1) {
            throw new BusinessException(ResultCode.LOCK_DAMAGED);
        }
        if (lock.getLockStatus() != null && lock.getLockStatus() == Constants.LockStatus.OPEN) {
            throw new BusinessException(ResultCode.LOCK_ALREADY_OPEN);
        }

        PowerRoom room = powerRoomMapper.selectById(lock.getRoomId());
        if (room == null) {
            throw new BusinessException(ResultCode.ROOM_NOT_EXIST);
        }

        Long currentUserId = SecurityUtils.getCurrentUserId();
        if (!userService.checkPermission(currentUserId, room.getRegionId())) {
            throw new BusinessException(ResultCode.FORBIDDEN);
        }

        lock.setLockStatus(Constants.LockStatus.OPEN);
        lock.setLastOperateTime(LocalDateTime.now());
        lock.setLastOperateUserId(currentUserId);
        nfcLockMapper.updateById(lock);

        saveOperationLog(lock, Constants.OperationType.OPEN, request.getLongitude(), request.getLatitude(),
                request.getRemark());

        log.info("开锁成功: {}, 操作人: {}", request.getLockNfcId(), currentUserId);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void closeLock(LockOperateRequest request) {
        NfcLock lock = nfcLockMapper.selectByNfcId(request.getLockNfcId());
        if (lock == null) {
            throw new BusinessException(ResultCode.LOCK_NOT_EXIST);
        }

        if (lock.getLockStatus() != null && lock.getLockStatus() == Constants.LockStatus.CLOSED) {
            throw new BusinessException(ResultCode.LOCK_ALREADY_CLOSED);
        }

        PowerRoom room = powerRoomMapper.selectById(lock.getRoomId());
        if (room == null) {
            throw new BusinessException(ResultCode.ROOM_NOT_EXIST);
        }

        Long currentUserId = SecurityUtils.getCurrentUserId();
        if (!userService.checkPermission(currentUserId, room.getRegionId())) {
            throw new BusinessException(ResultCode.FORBIDDEN);
        }

        LocalDateTime now = LocalDateTime.now();
        long unlockMinutes = 0;
        if (lock.getLastOperateTime() != null) {
            unlockMinutes = Duration.between(lock.getLastOperateTime(), now).toMinutes();
        }

        lock.setLockStatus(Constants.LockStatus.CLOSED);
        lock.setLastOperateTime(now);
        lock.setLastOperateUserId(currentUserId);
        nfcLockMapper.updateById(lock);

        OperationLog logEntry = buildOperationLog(lock, Constants.OperationType.CLOSE,
                request.getLongitude(), request.getLatitude());
        String remarkSuffix = request.getRemark() != null ? request.getRemark() : "";
        logEntry.setRemark(String.format("关锁成功，本次开锁时长 %d 分钟。%s", unlockMinutes, remarkSuffix));
        operationLogMapper.insert(logEntry);

        log.info("关锁成功: {}, 开锁时长 {} 分钟", request.getLockNfcId(), unlockMinutes);
    }

    @Override
    public IPage<LockListVO> getLockList(Integer pageNum, Integer pageSize, String keyword,
                                         Long roomId, Long regionId, Integer lockStatus) {
        Long currentUserId = SecurityUtils.getCurrentUserId();
        User currentUser = userMapper.selectById(currentUserId);

        Long filterRegionId = regionId;
        if (currentUser != null && currentUser.getRoleType() != null
                && currentUser.getRoleType() != Constants.RoleType.SUPER_ADMIN && regionId == null) {
            filterRegionId = currentUser.getRegionId();
        }

        Page<LockListVO> page = new Page<>(pageNum, pageSize);
        return nfcLockMapper.selectLockPage(page, keyword, roomId, filterRegionId, lockStatus);
    }

    @Override
    public LockListVO getLockDetail(Long lockId) {
        NfcLock lock = nfcLockMapper.selectById(lockId);
        if (lock == null) {
            throw new BusinessException(ResultCode.LOCK_NOT_EXIST);
        }

        PowerRoom room = powerRoomMapper.selectById(lock.getRoomId());
        Long currentUserId = SecurityUtils.getCurrentUserId();
        if (!userService.checkPermission(currentUserId, room != null ? room.getRegionId() : null)) {
            throw new BusinessException(ResultCode.FORBIDDEN);
        }

        Page<LockListVO> page = new Page<>(1, 1);
        IPage<LockListVO> result = nfcLockMapper.selectLockPage(page, lock.getLockNfcId(), null, null, null);
        if (result.getRecords().isEmpty()) {
            throw new BusinessException(ResultCode.LOCK_NOT_EXIST);
        }

        return result.getRecords().get(0);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void reportDamage(Long lockId, String remark) {
        NfcLock lock = nfcLockMapper.selectById(lockId);
        if (lock == null) {
            throw new BusinessException(ResultCode.LOCK_NOT_EXIST);
        }

        lock.setIsDamaged(1);
        nfcLockMapper.updateById(lock);

        PowerRoom room = powerRoomMapper.selectById(lock.getRoomId());

        WarningRecord warning = new WarningRecord();
        warning.setWarningType(Constants.WarningType.DAMAGED);
        warning.setLockId(lockId);
        warning.setRoomId(lock.getRoomId());
        warning.setRoomName(room != null ? room.getRoomName() : null);
        warning.setWarningContent(String.format("锁具【%s】已报修: %s", lock.getLockNfcId(), remark));
        warning.setStatus(0);

        warningRecordMapper.insert(warning);

        log.info("锁具报修成功: {}", lockId);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void deleteLock(Long lockId) {
        NfcLock lock = nfcLockMapper.selectById(lockId);
        if (lock == null) {
            throw new BusinessException(ResultCode.LOCK_NOT_EXIST);
        }

        PowerRoom room = powerRoomMapper.selectById(lock.getRoomId());
        Long currentUserId = SecurityUtils.getCurrentUserId();
        if (!userService.checkPermission(currentUserId, room != null ? room.getRegionId() : null)) {
            throw new BusinessException(ResultCode.FORBIDDEN);
        }

        nfcLockMapper.deleteById(lockId);
        log.info("删除锁具成功: {}", lockId);
    }

    private void saveOperationLog(NfcLock lock, Integer operationType,
                                  BigDecimal longitude, BigDecimal latitude, String remark) {
        OperationLog log = buildOperationLog(lock, operationType, longitude, latitude);
        log.setRemark(remark);
        operationLogMapper.insert(log);
    }

    private OperationLog buildOperationLog(NfcLock lock, Integer operationType,
                                           BigDecimal longitude, BigDecimal latitude) {
        Long currentUserId = SecurityUtils.getCurrentUserId();
        User user = userMapper.selectById(currentUserId);
        PowerRoom room = powerRoomMapper.selectById(lock.getRoomId());

        OperationLog log = new OperationLog();
        log.setOperationType(operationType);
        log.setUserId(currentUserId);
        log.setUserName(user != null ? user.getRealName() : null);
        log.setRegionId(user != null ? user.getRegionId() : null);
        log.setLockId(lock.getLockId());
        log.setLockNfcId(lock.getLockNfcId());
        log.setRoomId(lock.getRoomId());
        log.setRoomName(room != null ? room.getRoomName() : null);
        log.setLongitude(longitude);
        log.setLatitude(latitude);
        log.setOperationTime(LocalDateTime.now());

        return log;
    }
}

